import Ref from "./Ref.js";
import Line from "./RefLine.js";

/**
 * @implements RefI<`#${string}`>
 */
export default class RefColor extends Ref {

  /** @type { Ref<`#${string}`> } */
  ref;
  /** @type { Line } */
  red;
  /** @type { Line } */
  green;
  /** @type { Line } */
  blue;

  /**
   *
   * @param { `#${string}` } value
   * @param { number } length
   */
  constructor(value, length) {
    super();
    const num = this.parseColor(value);
    this.red = new Line(num >> 16 & 0xFF, length);
    this.green = new Line(num >> 8 & 0xFF, length);
    this.blue = new Line(num >> 0 & 0xFF, length);
    this.ref = new Ref(value);
  }

  get value() {
    return this.ref.value;
  }

  set value(val) {
    const changing = this.red.changing || this.green.changing || this.blue.changing;
    const num = this.parseColor(val);
    this.red.value = num >> 16 & 0xFF;
    this.green.value = num >> 8 & 0xFF;
    this.blue.value = num >> 0 & 0xFF;
    this.trigger();
    const changeValue = () => {
      requestAnimationFrame(() => {
        this.red.trigger();
        this.green.trigger();
        this.blue.trigger();
        this.trigger();
        if (this.red.changing || this.green.changing || this.blue.changing) {
          changeValue();
        }
      });
    }
    if (!changing) {
      changeValue();
    }
  }

  /**
   * @param { (newVal: any, oldVal: any) => void } func
   * @param { boolean } [deep]
   */
  bind(func, deep) {
    this.ref.bind(func, deep);
  }

  /**
   * @param { string } color
   */
  parseColor(color) {
    return parseInt(color.slice(1), 16);
  }

  /**
   * @param { number } num
   * @return { `#${string}` }
   */
  formatColor(num) {
    return `#${num.toString(16)}`;
  }

  trigger() {
    this.ref.value = this.formatColor(
      (Math.round(this.red.value) << 16) +
      (Math.round(this.green.value) << 8) +
      (Math.round(this.blue.value) << 0)
    );
  }
}
